package com.opengw.configuration;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.opengw.exception.OpenGWException;


public class CertificateManager {
	private static CertificateManager certificateManager;
	private HashMap<String, X509Certificate> certificateMap;
	
	static{
		try {
			certificateManager = new CertificateManager();
		} catch (Exception e) {
		}
	}
	
	private CertificateManager() throws NoSuchAlgorithmException, KeyManagementException {
	   
		if(certificateManager == null){
			this.certificateMap = new HashMap<String, X509Certificate>();
		
			SSLContext sc = SSLContext.getInstance("SSL");
			TrustManager[] trustManager = new TrustManager[]{
				new LocalTrustManager()
			};
			sc.init(null, trustManager, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
			HttpsURLConnection.setDefaultHostnameVerifier(new LocalHostnameVerifier());
		}
	}
	
	public static CertificateManager getCertificateManager(){
		return certificateManager;
	}
	
	public void addCertificate(X509Certificate cert) throws Exception{
		String certificateKey = getCertificateSubjectDNCN(cert);
		if(certificateKey != null)
			this.certificateMap.put(certificateKey, cert);
		else
			throw new OpenGWException(OpenGWException.UNKNOWN_EXCEPTION);
	}
	
	public String getCertificateSubjectDNCN(X509Certificate certificate){
		String certificateCN = certificate.getSubjectDN().getName();
		String[] subjectArr = certificateCN.split(",");
		for(String subjectVal : subjectArr){
			if(subjectVal.trim().startsWith("CN")){
				return subjectVal.substring(subjectVal.indexOf("=") + 1).trim();
			}
		}
		return null;
	}
	
	public String getCertificateIssuerDNCN(X509Certificate certificate){
		String certificateCN = certificate.getIssuerDN().getName();
		String[] subjectArr = certificateCN.split(",");
		for(String subjectVal : subjectArr){
			if(subjectVal.trim().startsWith("CN")){
				return subjectVal.substring(subjectVal.indexOf("=") + 1).trim();
			}
		}
		return null;
	}
	
	class LocalHostnameVerifier implements HostnameVerifier {
		public boolean verify(String urlHostName, SSLSession session) {
			return true;
		}
	}
	
	class LocalTrustManager implements X509TrustManager{

		@Override
		public void checkClientTrusted(X509Certificate[] certArr, String authType)
				throws CertificateException {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] certArr, String authType)
				throws CertificateException {
			boolean isServerValid = false;
			try{
				X509Certificate certificate = certArr[0];
				X509Certificate trustedCertificate = certificateMap.get(getCertificateSubjectDNCN(certificate));
				if(trustedCertificate != null){
					if(certArr.length > 1){
						isServerValid = trustedCertificate.equals(certificate);
						if(isServerValid){
							trustedCertificate.verify(certArr[1].getPublicKey());
						}
					} else {
						certificate.verify(trustedCertificate.getPublicKey());
					}
					isServerValid = true;
				} else {
					isServerValid = false;	
				}
				
			} catch (Exception e) {
				isServerValid = false;
			}

			
			
			if(!isServerValid)
				throw new CertificateException();
			
			
		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}
		
	}

}
